//==================================================================================================
//  TPlanObject class
// depends on:
// * no dependences
// -------------------------------------------------------------------------------------------------
// Summary:
// TPlanObject class represents object on the building map (e.g. shop in a mall)
// it contains some data that can be interesting to the user about the object
// -------------------------------------------------------------------------------------------------
// Constructor expects object with following fields:
// floorNumber    - number floor where the object is situated
// name           - name of the object
// description    - arbitrary description of the object
// adresses       - list of global addresses
// phone_numbers  - list of phone numbers associated with the object
// email_adresses - list of emails associated with the object
// sites          - list of sites associated with the object
// categories     - list of categories with which the object can be classfied

function TPlanObject(parameters){
    this.floorNumber    = parameters.floorNumber    || null;
    this.name           = parameters.name           || null;
    this.description    = parameters.description    || null;
    this.adresses       = parameters.adresses       || null;
    this.phone_numbers  = parameters.phone_numbers  || null;
    this.email_adresses = parameters.email_adresses || null;
    this.sites          = parameters.sites          || null;
    this.categories     = parameters.categories     || null;

}




//==================================================================================================
//    TPlanEngine class
// depends on:
// * TToolTip class
// * TPlanObject class
// -------------------------------------------------------------------------------------------------
// Summary:
// TPlanEngine class represents object that controls behavior of the plan widget
// -------------------------------------------------------------------------------------------------
// Constructor expects object with following fields:
// parentElementId        - obligatory parameter, id of the element in which will be placed building
// plan
// predefinedParentWidth  - not obligatory parameter, in most cases the class will be able to
// calculate parentWidth by itself, if it's impossible use this field (digital value without px)
// predefinedParentHeight - not obligatory parameter, in most cases the class will be able to
// calculate parentHeight by itself, if it's impossible use this field (digital value without px)
// NOTE: predefinedParentWidth and predefinedParentHeight have higher priority over real size of
// parent
function TPlanEngine(parameters){
//--------------------------------------------------------------------------------------------------
//  TPlanEngine global vars
    var dx = 0;
    var dy = 0;
    var parentElement = document.getElementById(parameters.parentElementId);
    var parentWidth;
    var parentHeight;
    var drag = null;
    var drag_style = null;
    var doc  = document.documentElement;
    var offset = 0;
    var step_h = 0;
    var step_w = 0;
    var svg_style = null;
    var use = null;
    var currentWidth = 0;
    var currentHeight = 0;
    var currentScale = 0;
    var useMouseWheel = true;
    var initLeft = 0;
    var initTop = 0;
    var body = $('#'+parameters.parentElementId);
    var self = this;
    var floors;
    this.tooltip = new TToolTip();
    var tooltip_dx = 0;
    var tooltip_dy = 0;
    var tooltip_cx = 1;
    var tooltip_cy = 1;
    this.was_moved = 0;
    var eventX;
    var eventY;
    var highlightedElements = [];
    var planObjects = null;
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  auxiliary fuctions
    function from_px(str){
        var result = str.substring(0, (str.length-2));
        return parseFloat(result);
    }
    function to_px(str){
        return (str+'px');
    }
    function from_percent(str){
        var result = str.substring(0, (str.length-1));
        return parseFloat(result);
    }
    function to_percent(str){
        return (str+'%');
    }
    function is_percent_value(value){
        return value.indexOf('%', value.length-1) != -1;
    }
    function is_px_value(value){
        return value.indexOf('px', value.length-2) != -1;
    }
    function nodes_to_array(nodes){
        var array = new Array();
        for(var i=0; i<nodes.length; i++){
            if(nodes[i].nodeName.substring(0, 1)!='#'){
                array.push(nodes[i]);
            }
        }
        return array;
    }
    function sort_floors(i,ii){
        return (i.getAttribute('floor_number') - ii.getAttribute('floor_number'));
    }
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  tooltip control functions
    function check_attribute(element, attribute_name){
        try{
            attribute_value = element.getAttribute(attribute_name);
            if((attribute_value == null) || (attribute_value == undefined) || (attribute_value == '')){
                return null;
            } else {
                return attribute_value;
            }
        }catch(error){
            // console.log('Error occured while trying to obtain dom element attribute:');
            // console.log(error);
            // console.log('Error was skipped');
            return null;
        }
    }
    this.parseElementInfo = function(id){
        var result_text='';
        var element = document.getElementById(id);
        var fields = ['name', 'description', 'adresses', 'phone_numbers', 'email_adresses', 'sites', 'categories']
        for(var i=0; i<fields.length; i++){
            var value = check_attribute(element, fields[i]);
            if(value){
                result_text += (value + '<br>');
            }
        }
        return result_text;
    }
    this.showToolTip = function(evt, id){
        evt.stopPropagation();
        var text = this.parseElementInfo(id);
        if(this.was_moved==0){
            tooltip_dx = eventX - drag.offsetLeft - $(parentElement).offset().left;
            tooltip_dy = eventY - drag.offsetTop  - $(parentElement).offset().top;
            tooltip_cx = tooltip_dx/from_px(svg_style.width);
            tooltip_cy = tooltip_dy/from_px(svg_style.height);           
            self.tooltip.showToolTip(eventX - $(parentElement).offset().left, eventY - $(parentElement).offset().top, text, parameters.parentElementId);
        }else{
            this.was_moved=0;
        }
    };
    this.setUpShowToolTipFunction = function(newMethod){
        //element id where event occured will be passed to the new method as parameter
        this.showToolTip = newMethod;
    };
    this.highlightElement = function(elementId){
        var elementToHighlight = $('#' + elementId)
        var highlightingElement = elementToHighlight.clone();
        highlightingElement.attr('id','highlighted_' + elementId);
        highlightingElement.css('fill', '#00ffff');
        highlightingElement.css('fill-opacity', '1');
        highlightingElement.css('fill-rule', 'nonzero');
        highlightingElement.css('stroke-width', '0px');
        highlightingElement.css('filter', 'url(#highlightingFilter)');
        highlightingElement.insertAfter(elementToHighlight);
        highlightedElements.push(elementId);
    };
    this.removeHighlighting = function(elementId){
        $('#highlighted_' + highlightedElements[i]).remove();
    };
    this.removeAllHighlightings = function(){
        for(i in highlightedElements){
            this.removeHighlighting(highlightedElements[i]);
        }
        highlightedElements = [];
    };
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  drag'n'drop functions
    function down(event){
        this.was_moved = 0;
        eventX = event.clientX;
        eventY = event.clientY;
        drag_style.cursor='move';
        dx=event.pageX-drag.offsetLeft;
        dy=event.pageY-drag.offsetTop;
        drag.onmousemove = move;
        drag.touchmove   = move;
        drag.onmouseup  = up;
        drag.ontouchend = up;
    }
    function move(event){
        this.was_moved = 1;
        var left = event.pageX - dx;
        var top  = event.pageY - dy;
        //horizontal restrictions
        if(parentWidth >= from_px(svg_style.width)){
            if( from_px(drag_style.left) <= left ){
                drag_style.left = to_px(Math.min(parentWidth-from_px(svg_style.width) ,left));
            }else{
                drag_style.left = to_px(Math.max(0,left));
            }
        }else{
            if( from_px(drag_style.left) <= left ){
                drag_style.left = to_px(Math.min(0,left));
            }else{
                drag_style.left = to_px(Math.max(parentWidth-from_px(svg_style.width) ,left));
            }
        }
        //vertical restrictions
        if(parentHeight >= from_px(svg_style.height)){
            if( from_px(drag_style.top) <= top ){
                drag_style.top = to_px(Math.min(parentHeight-from_px(svg_style.height) ,top));
            }else{
                drag_style.top = to_px(Math.max(0,top));
            }
        }else{
            if( from_px(drag_style.top) <= top ){
                drag_style.top = to_px(Math.min(0,top));
            }else{
                drag_style.top = to_px(Math.max(parentHeight-from_px(svg_style.height) ,top));
            }
        }
        //tooltip position correction
        if(self.tooltip.isShown==1){
            self.tooltip.moveToolTipToNewPosition(from_px(drag_style.left)+tooltip_dx,from_px(drag.style.top)+tooltip_dy);
        }
    }
    function up(event){
        drag.onmousemove = null;
        drag.onmouseup = null;
        drag_style.cursor = 'default';
    }
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  zoom functions
    function prepare_zoom(){
        svg_style = document.getElementById('plan').style;
        drag_style = drag.style;
        var scale_attr = use.getAttribute('transform');
        if(scale_attr!=null){
            currentScale  = parseFloat(scale_attr.substring(6,scale_attr.length-1));
        }else{
            currentScale  = 1.0;
        }
        currentWidth = from_px(document.getElementById('plan').style.width);
        currentHeight = from_px(document.getElementById('plan').style.height);
    }
    this.zoom_in = function(){
        prepare_zoom();
        var width = currentWidth + step_w;
        var height = currentHeight + step_h;
        svg_style.width  = to_px(width);
        svg_style.height = to_px(height);
        use.setAttribute('transform', 'scale('+(currentScale+0.1)+')');
        tooltip_dx = width * tooltip_cx;
        tooltip_dy = height * tooltip_cy;
        self.tooltip.moveToolTipToNewPosition(tooltip_dx + from_px(drag_style.left), tooltip_dy + from_px(drag_style.top));
    }
    this.zoom_out = function(){
        prepare_zoom();
        var width = currentWidth - step_w;
        var height = currentHeight - step_h;
        svg_style.width  = to_px(width);
        svg_style.height = to_px(height);
        use.setAttribute('transform', 'scale('+(currentScale-0.1)+')');
        tooltip_dx = width * tooltip_cx;
        tooltip_dy = height * tooltip_cy;
        self.tooltip.moveToolTipToNewPosition(tooltip_dx + from_px(drag_style.left), tooltip_dy + from_px(drag_style.top));
    }
    function auto_zoom(){
        var body_height = 0.94*parentHeight - drag.offsetTop;
        if(currentHeight<body_height)
        {
            for(;(currentHeight+step_h)<=body_height;){
                self.zoom_in();
            }
        }else{
            for(;(currentHeight-step_h)>=body_height;){
                self.zoom_out();
            }
        }
    }
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  floor switch function
    function switchFloor(){
        self.tooltip.hideToolTip();
        if($.browser.opera){
            use.setAttribute('href','#'+this.getAttribute('floorId'));
        }else{
            use.setAttribute('xlink:href','#'+this.getAttribute('floorId'));
        }

    }
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  control setup functions
    function setUpZoomInButton(){
        var zoomInButton = document.createElement('img');
        zoomInButton.id = 'plus';
        zoomInButton.src = 'http://betadesktop.tapnmap.ru/img/plus.svg';
        zoomInButton.style.zIndex = 2;
        parentElement.appendChild(zoomInButton);
        zoomInButton.onclick = self.zoom_in;
        return zoomInButton;
    }
    function setUpZoomOutButton(){
        var zoomOutButton = document.createElement('img');
        zoomOutButton.id = 'minus';
        zoomOutButton.src = 'http://betadesktop.tapnmap.ru/img/minus.svg';
        zoomOutButton.style.zIndex = 2;
        parentElement.appendChild( zoomOutButton );
        zoomOutButton.onclick = self.zoom_out;
        return zoomOutButton;
    }
    function setUpFloorSwitсhButtons(){
        floors = nodes_to_array(document.getElementById('floors').childNodes);
        floors.sort(sort_floors);
        if (floors.length > 1){
            for(var i=0; i<floors.length; i++){
                var new_floor_switch = document.createElement('div');
                new_floor_switch.innerHTML = floors[i].getAttribute('floor_number');
                new_floor_switch.setAttribute('floorId',floors[i].id);
                var new_floor_switch_style = new_floor_switch.style;
                new_floor_switch_style.position = 'absolute';
                new_floor_switch_style.left = to_px(offset);
                var top_position = parentHeight - (i+1)*offset - offset;
                if(i==0){
                    new_floor_switch_style.top = to_px(top_position);
                }else{
                    new_floor_switch_style.top = to_px(top_position - offset*0.2);
                }
                new_floor_switch_style.width = to_px(offset);
                new_floor_switch_style.height = to_px(offset);
                new_floor_switch_style.zIndex = 2;
                new_floor_switch_style.backgroundColor = 'black';
                new_floor_switch_style.color = 'white';
                new_floor_switch_style.textAlign = 'center';
                new_floor_switch_style.borderRadius = '30%';
                new_floor_switch.onclick = switchFloor;
                parentElement.appendChild(new_floor_switch);
            }
        }
    }
    function setUpControls(){
        offset = Math.min( parentWidth, parentHeight) * 0.05; //offset from the screen border

        //zoom out button
        var minus = setUpZoomOutButton();
        minus.height = offset;
        minus.width  = offset;
        minus.style.position='absolute';
        minus.style.top  = to_px( 2*offset + Math.min(parentWidth, parentHeight) * 0.01 );
        minus.style.left = to_px( offset );

        //zoom in button
        var plus = setUpZoomInButton();
        plus.height = offset;
        plus.width  = offset;
        plus.style.position='absolute';
        plus.style.top  = to_px( offset );
        plus.style.left = to_px( offset );

    }
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  plan initialization functions
    function prepareParentElement(){
        parentElement.style.overflow = 'hidden';
        parentElement.scroll='no';
    }
    function createDragDiv(){
        var dragDiv = document.createElement('div');
        dragDiv.id = 'drag';
        dragDiv.style.position = 'absolute';
        dragDiv.style.zIndex = 1;
        parentElement.appendChild(dragDiv);
        return dragDiv;
    }
    this.setUpPlanFromFile = function(fileName){
        alert("Not supported method!");
        throw new Exception("Not supported method!");
    }
    function parsePlanFile(){
        planObjects = new Array();
        for(i in floors)
        {
            var nameElements = $(floors[i]).find('[name]');
            var floorNumber = check_attribute(floors[i], 'floor_number');
            for(j in nameElements){
                var name = check_attribute(nameElements[j], 'name');
                var description = check_attribute(nameElements[j], 'description');
                if(name){
                    planObjects.push(new TPlanObject({
                        floorNumber: floorNumber,
                        name:        name,
                        description: description,
                    }));
                }

            }
        }
    }
    this.setUpPlanFromText = function(planText){
        drag.innerHTML = planText;
        use = document.getElementById('use');
        prepare_zoom();
        step_h = Math.floor(from_px(svg_style.height) * 0.1 );
        step_w = Math.floor(from_px(svg_style.width)  * 0.1 );
        auto_zoom();
        setUpFloorSwitсhButtons();
        if(useMouseWheel){
            body.mousewheel(function(event, delta) {
                if(delta<0){
                    self.zoom_out();
                }else{
                    self.zoom_in();
                }
            });
        }
        parsePlanFile();
        var svgRootElement = $('#plan');
    }
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
// service functions
    this.searchPlanObjectsByName = function(pattern){
        var searchResult = new Array();
        var regExpPattern = new RegExp(pattern, 'i');
        for(i in planObjects){
            if(planObjects[i].name.search(regExpPattern) >= 0){
                searchResult.push(planObjects[i]);
            }
        }
        return searchResult;
    }
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  initialization of the engine
    if( parameters.predefinedParentWidth == null || parameters.predefinedParentWidth == undefined ){
        parentWidth = parentElement.clientWidth;
    } else {
        parentWidth = parameters.predefinedParentWidth;
    }
    if( parameters.predefinedParentHeight == null || parameters.predefinedParentHeight == undefined ){
        parentHeight = parentElement.clientHeight;
    } else {
        parentHeight = parameters.predefinedParentHeight;
    }
    prepareParentElement();
    setUpControls();
    drag = createDragDiv();
    drag_style=drag.style;

    drag_style.left=to_px(initLeft);
    drag_style.top =to_px(initTop);
    body.mousewheel(function(event, delta){});
    drag.onmousedown  = down;
    drag.ontouchstart = down;
    parentElement.style.overFlow = 'hidden';
    $(parentElement).bind('click', function(evt){
        self.removeAllHighlightings();
    });
//--------------------------------------------------------------------------------------------------
};

//==================================================================================================
